#include "Network.h"
#include <QDebug>

Network::Network(QObject* p, const QString& serverIP, quint32 serverPort) : QObject(p), m_serverIP(serverIP), m_serverPort(serverPort)
{
    m_socket = new QTcpSocket(this);
    connect(m_socket, SIGNAL(readyRead()), this, SLOT(dataReceived()));
    connect(m_socket, SIGNAL(connected()), this, SLOT(connected()));
    connect(m_socket, SIGNAL(disconnected()), this, SLOT(disconnected()));
    connect(m_socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(socketError(QAbstractSocket::SocketError)));

    m_size = 0;

    QTimer *timer = new QTimer(this);
    connect(timer, SIGNAL(timeout()), this, SLOT(send()));
    timer->start(10);
}


void Network::send()
{
    if(m_toSend.isEmpty())
        return;
    if(m_socket->bytesToWrite() > 0)
        return;


    Data *d = m_toSend.dequeue();
    while(1)
    {
        if(!d->isTooLate())
        {
            qDebug() << "Sending...";
            m_socket->write(d->data);
            break;
        }
        if(m_toSend.isEmpty())
            break;
        delete d;
        d = m_toSend.dequeue();
    }
    delete d;
}



void Network::dataReceived()
{
    QDataStream in(m_socket);
    if (m_size == 0)
    {
        if (m_socket->bytesAvailable() < (int)sizeof(quint32))
             return;

        in >> m_size;
    }

    if (m_socket->bytesAvailable() < m_size)
        return;
    quint32 r;
    in >> r;
    int req = r;

    switch (req)
    {
        case PHASE_CHANGED:
        {
            quint32 ph = 0, ply = 0;
            in >> ph >> ply;
            int pha = ph;
            emit phaseChanged(PHASE(pha), ply);
        }
        break;
        case PLAYER_INFOS:
        {
            PlayerInfos inf;
            in >> inf;
            emit playerInfos(inf);
        }
        break;
        case STATE_INFOS:
        {
            StateInfos inf;
            in >> inf;
            emit stateInfos(inf);
        }
        break;
        case ALL_STATES_INFOS:
        {
            QList<StateInfos> inf;
            in >> inf;
            emit allStatesInfos(inf);
        }
        break;
        case ALL_PLAYERS_INFOS:
        {
            QList<PlayerInfos> inf;
            in >> inf;
            emit allPlayersInfos(inf);
        }
        break;
        case ERROR:
        {
            quint32 ph = 0;
            QStringList args;
            in >> ph >> args;
            int pha = ph;
            emit error(ERROR_CODE(pha), args);
        }
        break;
        case INFOS:
        {
            quint32 ph = 0;
            QStringList args;
            in >> ph >> args;
            int pha = ph;
            emit info(INFOS_CODE(pha), args);
        }
        break;
        case REQUEST_DICE_ROLL:
        {
            bool def;
            in >> def;
            emit requestDiceRoll(def);
        }
        break;
        case PLAYER_DEFEATED:
        {
            quint32 pl;
            in >> pl;
            emit playerDefeated(pl);
        }
        break;
        case GAME_STARTED:
        {
            quint32 n;
            in >> n;
            emit gameStarted(n);
        }
        break;
        case LOBBY_STATE_CHANGED:
        {
            bool n;
            in >> n;
            emit lobbyStateChanged(n);
        }
        break;
        default:
        {
            qDebug() << "WTF ?";
        }
        break;
    }
    qDebug() << "Data received and read.";

    QString message;
    in >> message;

    m_size = 0;
}

void Network::connexion()
{
    emit connecting();

    m_socket->abort();
    m_socket->connectToHost(m_serverIP, m_serverPort);
}

void Network::disconnect()
{
    m_socket->disconnectFromHost();
}

void Network::connected()
{

    emit connexionEtablished();
}

void Network::disconnected()
{

    emit connexionLost();
}

void Network::socketError(QAbstractSocket::SocketError err)
{
    qDebug() << "Socket error : " << m_socket->errorString();
}

namespace {

void Network::changeLobbyState()
{
    Data* d = new Data;
    QDataStream out(&d->data, QIODevice::WriteOnly);

    out << (quint32) 0;
    out << (quint32) REQUEST_LOBBY_STATE_CHANGE;
    out.device()->seek(0);
    out << (quint32) (d->data.size() - sizeof(quint32));
    m_toSend.enqueue(d);
}

void Network::requestUpdate()
{
    Data* d = new Data;
    QDataStream out(&d->data, QIODevice::WriteOnly);

    out << (quint32) 0;
    out << (quint32) REQUEST_UPDATE;
    out.device()->seek(0);
    out << (quint32) (d->data.size() - sizeof(quint32));
    m_toSend.enqueue(d);
}

void Network::requestNextPhase()
{
    Data* d = new Data;
    QDataStream out(&d->data, QIODevice::WriteOnly);

    out << (quint32) 0;
    out << (quint32) REQUEST_NEXT_PHASE;
    out.device()->seek(0);
    out << (quint32) (d->data.size() - sizeof(quint32));
    m_toSend.enqueue(d);
}

void Network::requestPlayerInfos(quint32 player)
{
    Data* d = new Data;
    QDataStream out(&d->data, QIODevice::WriteOnly);

    out << (quint32) 0;
    out << (quint32) REQUEST_PLAYER_INFOS;
    out << (quint32) player;
    out.device()->seek(0);
    out << (quint32) (d->data.size() - sizeof(quint32));
    m_toSend.enqueue(d);
}

void Network::requestStateInfos(QString state)
{
    Data* d = new Data;
    QDataStream out(&d->data, QIODevice::WriteOnly);

    out << (quint32) 0;
    out << (quint32) REQUEST_STATE_INFOS;
    out << state;
    out.device()->seek(0);
    out << (quint32) (d->data.size() - sizeof(quint32));
    m_toSend.enqueue(d);
}

void Network::requestMovement(QString from, QString to, quint32 nbr)
{
    Data* d = new Data;
    QDataStream out(&d->data, QIODevice::WriteOnly);

    out << (quint32) 0;
    out << (quint32) REQUEST_MOVEMENT;
    out << from;
    out << to;
    out << nbr;
    out.device()->seek(0);
    out << (quint32) (d->data.size() - sizeof(quint32));
    m_toSend.enqueue(d);
}

void Network::requestAttack(QString from, QString to, quint32 nbr, bool a)
{
    Data* d = new Data;
    QDataStream out(&d->data, QIODevice::WriteOnly);

    out << (quint32) 0;
    out << (quint32) REQUEST_ATTACK;
    out << from;
    out << to;
    out << nbr;
    out << a;
    out.device()->seek(0);
    out << (quint32) (d->data.size() - sizeof(quint32));
    m_toSend.enqueue(d);
}

void Network::requestReinforcement(QString state, quint32 nbr)
{
    Data* d = new Data;
    QDataStream out(&d->data, QIODevice::WriteOnly);

    out << (quint32) 0;
    out << (quint32) REQUEST_REINFORCEMENT;
    out << state;
    out << nbr;
    out.device()->seek(0);
    out << (quint32) (d->data.size() - sizeof(quint32));
    m_toSend.enqueue(d);
}

}
